import { Injectable, EventEmitter } from "@angular/core";
import { BaseHttpService } from "../../shared/services/baseHttp.service";
import { HttpClient, HttpParams } from "@angular/common/http";
import { Observable } from "rxjs/Observable";
import { FilterModel, FilterType } from "./models/filter.model";
import { SettingValueModel } from "./models/settingValue.model";
import { SettingModel } from "./models/setting.model";
import { StorageService } from "../../shared/services/storage.service";
import { ReplaySubject } from "rxjs";
import { TemplateCriteriaModel } from "./models/templateCriteria.model";
import { BaseTemplateOutput } from "./filter/criteria/template/models/save/criteriaTemplateOutput.models";
import { Profile } from "../../framework/login/models/profile.model";
import { AppComponent } from "../../app.component";

export enum TargetType {
    Screening = 0,
    Report = 1
}

@Injectable()
export class ReportingService extends BaseHttpService {
    private criteraLevelsSubject = new ReplaySubject<any[]>(1);
    public levels = this.criteraLevelsSubject.asObservable();

    private criteraDetailsSubject = new ReplaySubject<any>(1);
    public details = this.criteraDetailsSubject.asObservable();    

    constructor(private http: HttpClient) {
        super();
    }

    public getFilters(target: TargetType): Observable<FilterModel[]> {
        let params: HttpParams = new HttpParams();
        params = params.set("target", TargetType[target])
            .set("withExtendedData", true.toString());

        return this.http.get<FilterModel[]>(this.serverUrl + '/screeningtoolserv/api/v1/filters', { params: params });
    }

    public updateFilter(filter: FilterModel, saveSettings: boolean): Observable<FilterModel> {
        if (saveSettings)
            return this.http.put<FilterModel>(this.serverUrl + '/screeningtoolserv/api/v1/filters', filter);
        else
            return this.http.post<FilterModel>(this.serverUrl + '/screeningtoolserv/api/v1/filters', filter);
    }

    public deleteFilter(filterId: string): Observable<void> {
        return this.http.delete<void>(this.serverUrl + '/screeningtoolserv/api/v1/filters/' + filterId);
    }

    public getFilter(filterId: string): Observable<FilterModel> {
        let userInfo = StorageService.get().userInfo;
        let params: HttpParams = new HttpParams();
        params = params.set("userId", userInfo.userId)
            .set("accountId", userInfo.accountId)
            .set("includeAllDetails", true.toString());

        return this.http.get<FilterModel>(this.serverUrl + '/screeningtoolserv/api/v1/filters/' + filterId, { params: params });
    }

    public getSettingValues(term: string, setting: SettingModel): Observable<SettingValueModel[]> {
        let listOptions: any = {
            skip: setting.skip,
            take: setting.take,
            orderBy: ['Name'],
            orderAscending: true,
            includeAllFields: false,
            getCount: false
        }

        let params: HttpParams = new HttpParams();
        params = params.set('collItemsNameLike', term);
        params = this.urlEncodeObject(listOptions, params);

        return this.http.get<SettingValueModel[]>(this.serverUrl + '/screeningtoolserv/api/v1/filters/null/criteria/null/collection/' + setting.name + '/content?', { params: params });
    }

    public populateOutputResult(filterId: string): Observable<any> {
        return this.http.post<any>(this.serverUrl + '/screeningtoolserv/api/v1/filters/' + filterId + '/criteria/', []);
    }

    public getUniversesAndPortfolios(term: string, skip: number, take: number): Observable<any[]> {
        let userInfo = StorageService.get().userInfo;
        let listOptions: any = {
            skip: skip,
            take: take,
            orderBy: ['Name'],
            orderAscending: true,
            includeAllFields: false,
            getCount: false
        }

        let params: HttpParams = new HttpParams();
        params = params.set('isMyPortfolio', true.toString())
            .set('namePortfolio', term ? term : '')
            .set('sharingStatus', null)
            .set('isNameEqual', false.toString())
            .set('accountId', userInfo.accountId)
            .set('userId', userInfo.userId);
        params = this.urlEncodeObject(listOptions, params);

        return this.http.get<any[]>(this.serverUrl + '/screeningtoolserv/api/v1/portfolios/universesandportfolios', { params: params });
    }

    public deleteCriteria(filterId: string, criteriaId: string): Observable<any> {
        return this.http.delete(this.serverUrl + 'screeningtoolserv/api/v1/filters/' + filterId + '/criteria/' + criteriaId, { responseType: 'text' });
    }

    public deleteCriteriaGroup(filterId: string, criteriaId: string, criteriaGroupId: string): Observable<any> {
        let params: HttpParams = new HttpParams();
        params = params.set('filterCriteriaId', criteriaId)
            .set('filterGroupId', criteriaGroupId);
        return this.http.patch(this.serverUrl + '/screeningtoolserv/api/v1/filters/' + filterId + '/criteria/', null,
            { params: params, responseType: 'text' });
    }

    public getCriteriaLevels(): void {
        let params: HttpParams = new HttpParams();
        params = params.set('includeAllFields', true.toString());

        this.http.get<any[]>(this.serverUrl + '/screeningtoolserv/api/v1/criterialevel1', { params: params }).subscribe(data => {
            this.criteraLevelsSubject.next(data);
        });
    }

    public getTemplateCriteria(criteriaLevel1: string, criteriaLevel2: string, criteriaLevel3: string): Observable<TemplateCriteriaModel[]> {
        return this.http.get<TemplateCriteriaModel[]>(this.serverUrl + '/screeningtoolserv/api/v1/criterialevel1/' + criteriaLevel1 + '/criterialevel2/' + criteriaLevel2 + '/criterialevel3/' + criteriaLevel3 + '/templatecriteria');
    }

    public getCriteria(criteriaId: string, type: number, isTemplate: boolean): Observable<any> {
        let params: HttpParams = new HttpParams();
        params = params.set('CriteriaType', type.toString())
            .set('isTemplate', isTemplate.toString());
        return this.http.get<any>(this.serverUrl + '/screeningtoolserv/api/v1/criteria/' + criteriaId, { params: params });
    }

    public getCriteriabyName(criteriaId: string, type: number, term: string): Observable<any> {
        let params: HttpParams = new HttpParams();
        params = params.set('CriteriaType', type.toString())
            .set('loadMore', false.toString())
            .set('searchText', term)
            .set('skip', '0')
            .set('take', '20');
        return this.http.get<any>(this.serverUrl + '/screeningtoolserv/api/v1/criteria/' + criteriaId, { params: params });
    }

    public getCriteriaDetails(filterId: string, criteriaId: string): void {
        this.http.get<any>(this.serverUrl + '/screeningtoolserv/api/v1/filters/' + filterId + '/criteria/' + criteriaId).subscribe(data => {
            this.criteraDetailsSubject.next(data);
        })
    }

    //TODO: change back-end so it receives the same structure of the base class that all criteria extends (see criteriaType 1)
    public addCriteria(filterId: string, criteria: BaseTemplateOutput[], type: TargetType): Observable<any> {
        let body: string[] = [];       
        
        criteria.forEach(element => {
            //this is required because the back-end expects a different structure for criteriaType 1
            if(element.type == 1 && type == TargetType.Report)
            {
                element.isNotScreenableCriteriaItems.forEach(unscreenable => {
                    unscreenable.type = 1;
                    body.push(JSON.stringify(unscreenable));    
                });                
            }
            else
                body.push(JSON.stringify(element));
        });

        return this.http.post<any>(this.serverUrl + '/screeningtoolserv/api/v1/filters/' + filterId + '/criteria/', body);
    }

    public updateCriteria(filterId: string, filterGroupId: string, criteria: BaseTemplateOutput): Observable<any> {
        let params: HttpParams = new HttpParams();
        params = params.set('criteriaType', criteria.type.toString())
            .set('criteriaLevel2Id', criteria.criteriaLevel2Id)
            .set('filterGroupId', filterGroupId);
        return this.http.put<any>(this.serverUrl + '/screeningtoolserv/api/v1/filters/' + filterId + '/criteria/', criteria.dataToSave(), { params: params });
    }

    public getInvolvementCategories(indicator: string): Observable<any[]> {
        return this.http.get<any[]>(this.serverUrl + 'productinvolvementserv/api/v1/piindicator/' + indicator + '/categories');
    }

    public getSustainableProductsCategories(indicator: string): Observable<any[]> {
        return this.http.get<any[]>(this.serverUrl + 'sustainableproductsserv/api/v1/spframework/' + indicator + '/categories');
    }

    public generateFilterResults(filterId: string, portfolioId: string, universeId: string, report: boolean, refreshOutput: boolean): Observable<void> {
        return this.http.put<void>(this.serverUrl + '/screeningtoolserv/api/v1/runs/' + filterId, {
            filterId: filterId,
            PortfolioId: portfolioId,
            UniverseId: universeId,
            Report: report,
            RefreshOutput: refreshOutput
        });
    }

    public getResults(filterId: string, getCount: boolean, orderAscending: boolean, orderBy: string, skip: number, take: number, isValid?: number, portfolioId?: string): Observable<any> {
        let params: HttpParams = new HttpParams();
        params = params.set('getCount', getCount.toString())
            .set('orderAscending', orderAscending.toString())
            .set('orderBy', orderBy)
            .set('skip', skip.toString())
            .set('take', take.toString());

        if (isValid != undefined)
            params = params.set('isValid', isValid != null ? isValid.toString() : null);
        if (portfolioId)
            params = params.set('portfolioId', portfolioId);


        return this.http.get<any>(this.serverUrl + '/screeningtoolserv/api/v1/runs/' + filterId + '/results', { params: params });
    }

    public reorderResultColumns(filterId: string, criteriaIds: string[]): Observable<void> {
        return this.http.patch<void>(this.serverUrl + '/screeningtoolserv/api/v1/filters/', {
            resourceId: filterId,
            orderedCriteriaIds: criteriaIds
        });
    }

    public getOwnerDetails(ownerId: string): Observable<Profile> {
        return this.http.get<Profile>(this.serverUrl + '/accountsserv/api/v1/users/' + ownerId);
    }
  
    public loadDataPointsForReport(level2: string, listChecks: any[]): Observable<any[]> {
        let params: HttpParams = new HttpParams();
        params = params.set('criteriaLevel2Id', level2);

        let body: string[] = [];
        listChecks.forEach(element => {
            body.push(JSON.stringify(element));
        });

        return this.http.post<any[]>(this.serverUrl + '/screeningtoolserv/api/v1/criteria', body, { params: params });
    }

    public shareFilter(filter: FilterModel) {
        filter.type = filter.shared ? FilterType.Private : FilterType.Shared;
        this.updateFilter(filter, false).subscribe(_ => { });
    }

    public filterRemovedNotification(rootComponent: AppComponent) {
        rootComponent.showSuccess('Filter has been removed', 'Success');
    }
}
